{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Budget accounting with diffprivlib"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Diffprivlib includes a budget accountant to allow you to keep track of privacy budget being spent. The budget accounting is handled by the `BudgetAccountant` class."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Basic functionality of the `BudgetAccountant` includes initialisation with an appropriate `epsilon` and `delta` (if desired), determining the total current spend with `total()` and evaluating the remaining budget to be spent with `remaining()`. The number of recorded budget spends can be found using `len()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from numpy.random import random\n",
    "from diffprivlib import BudgetAccountant\n",
    "from diffprivlib.tools import mean, var\n",
    "\n",
    "X = random(100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total spent: (epsilon=1.0, delta=0.0)\n",
      "Remaining budget (for 1 query): (epsilon=4.0, delta=0.0)\n",
      "Remaining budget (for 4 queries): (epsilon=1.0, delta=0.0)\n",
      "Number of queries recorded: 1\n"
     ]
    }
   ],
   "source": [
    "acc = BudgetAccountant(epsilon=5, delta=0)\n",
    "dp_mean = mean(X, bounds=(0, 1), accountant=acc)\n",
    "\n",
    "print(\"Total spent: %r\" % (acc.total(),))\n",
    "print(\"Remaining budget (for 1 query): %r\" % (acc.remaining(),))\n",
    "print(\"Remaining budget (for 4 queries): %r\" % (acc.remaining(4),))\n",
    "print(\"Number of queries recorded: %d\" % len(acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If a query were to exceed the privacy budget specified in the accountant, an error is raised and execution ceases."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error raised <class 'diffprivlib.utils.BudgetError'>: Privacy spend of (1,0) not permissible; will exceed remaining privacy budget. Use BudgetAccountant.remaining() to check remaining budget.\n"
     ]
    }
   ],
   "source": [
    "acc = BudgetAccountant(1.5, 0)\n",
    "\n",
    "dp_mean = mean(X, epsilon=1, bounds=(0, 1), accountant=acc)\n",
    "\n",
    "try:\n",
    "    dp_std = var(X, epsilon=1, bounds=(0, 1), accountant=acc)\n",
    "except Exception as e:\n",
    "    print(\"Error raised {}: {}\".format(type(e), e))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using `BudgetAccountant` to track privacy budget"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are three ways to use the `BudgetAccountant` class to track budget spend across many operations:\n",
    "1. **Parametrisation:** Passed as a parameter (`accountant=acc`)\n",
    "2. **Default:** Set as a default (`set_default()`)\n",
    "3. **Context manager:** Using \"`with`\" over a block of code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total spend: (epsilon=1.618, delta=0.0)\n"
     ]
    }
   ],
   "source": [
    "acc_p = BudgetAccountant()\n",
    "mean(X, epsilon=1.618, bounds=(0, 1), accountant=acc_p)\n",
    "\n",
    "print(\"Total spend: %r\" % (acc_p.total(),))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total spend: (epsilon=2.718, delta=0.0)\n"
     ]
    }
   ],
   "source": [
    "acc_d = BudgetAccountant()\n",
    "acc_d.set_default()\n",
    "mean(X, epsilon=2.718, bounds=(0, 1))\n",
    "\n",
    "print(\"Total spend: %r\" % (acc_d.total(),))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total spend: (epsilon=3.141, delta=0.0)\n"
     ]
    }
   ],
   "source": [
    "with BudgetAccountant() as acc_w:\n",
    "    mean(X, epsilon=1.5705, bounds=(0, 1))\n",
    "    var(X, epsilon=1.5705, bounds=(0, 1))\n",
    "\n",
    "print(\"Total spend: %r\" % (acc_w.total(),))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Setting the `slack`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Composition of privacy budgets typically add up linearly, that is unless you allow a slack in your `delta`. This is governed by the `slack` parameter in the initialisation.\n",
    "\n",
    "The benefit of a non-zero slack is especially evident when many queries are being asked."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7f33097e06a0>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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4ti1axdBQUEAjBgxgu7du/P2228DsHDhQiZMmADA559/Tv369cnJySE0NJQRI0bg6elJVlYW4eHhvP7667z44ot8+umnTJw4sdQ21XFxcSxcuJD169fj4uLC448/zjfffMOAAQN45ZVXiIqKwt3dnRtvvJGuXbtesj3SQtz+WogXSUpKwtvb+5LlLjf9akJCQli7dm2Z+mOJS5lMmh+iEnljeRyZuQU83rcNT/XzpWb1sl/TUFEcLyBsJDIykqeffhqAUaNGERkZSXBwMGvWrGHs2LEABAQEEBAQAEDDhg3x8fFh48aN+Pr6smfPHnr06AHABx98wKJFiwBISEhg//79eHp6Ur169eL21cHBwfzxxx9A6W2qv/rqK6KioggNDQWMPZlGjRqxadOmC9qAjxw5kn379l2yPdJC3P5aiFtLo0aNrnheTVzdvhNnmbAomi2H0wltVY/XI/xp17iOrcu6KscLiKt80reGtLQ0/v77b6Kjo1FKUVhYiFKqeO/gckaNGsV3331Hhw4diIiIQCnFqlWr+PPPP9mwYQNubm707du3uJV1yTbdl2t9XURrzQMPPMB///vfC6aXdkiiPEgLcdu3EC/SvHlzEhISLlmuefPmF9SVmJhI3759r1gnSGvy65FzrpAP/t7Pp2viqe3qzFsjArgj2Itq1Sr+moZrIecgysEPP/zAfffdx5EjRzh8+DAJCQm0bt2atWvX0rt37+IW0TExMezatav4eRERESxZsoTIyEhGjRoFGIdR6tWrh5ubG3v27GHjxo1X/f2ltam+6aab+OGHHzh58iRghNiRI0fo1q0bq1evJjU1lfz8/OJW4heTFuL210K8yNChQ5k/fz5aazZu3Ii7uztNmzZl4MCB/P7776Snp5Oens7vv/9evKdzJdKa/Nqs3HOSATNX88mqgwzv2py/n+/LXaHedhMOIAFRLiIjI4mIiLhg2ogRI4iMjOSxxx4jMzOTjh07MnnyZIKDg4uXqVevHh07duTIkSPFb16DBg2ioKCAjh07Mn78eMLDw6/6+0trU92pUydee+01br75ZgICAhgwYADJyck0bdqUKVOm0L17d3r06HHZXjrSQty+WojPnj2b2bNnAzB48GB8fHxo27Ytjz76KB9//DFg3Ktk0qRJhIaGEhoayuTJk6lfvz4AL774Il5eXmRnZ+Pl5XXBTa3W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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "acc_n = BudgetAccountant()\n",
    "acc_a = BudgetAccountant(slack=1e-3)\n",
    "epsilon, queries = 2**-6, 30\n",
    "\n",
    "budget_naive = [0] + [acc_n.spend(epsilon, 0).total()[0] for i in range(queries)]\n",
    "budget_advanced = [0] + [acc_a.spend(epsilon, 0).total()[0] for i in range(queries)]\n",
    "\n",
    "plt.plot(range(queries + 1), budget_naive, label=\"Naive composition (slack=%g)\" % acc_n.slack)\n",
    "plt.plot(range(queries + 1), budget_advanced, label=\"Advanced composition (slack=%g)\" % acc_a.slack)\n",
    "plt.xlabel(\"Queries\")\n",
    "plt.ylabel(\"Epsilon spent\")\n",
    "plt.xlim(0, queries)\n",
    "plt.ylim(0, None)\n",
    "plt.legend()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
